package com.summer.boss.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.summer.boss.auth.JwtInfo;
import com.summer.boss.auth.JwtUtil;
import com.summer.boss.constant.CommonConstants;
import com.summer.boss.enums.ResponseCodeEnum;
import com.summer.boss.convert.MenuConvert;
import com.summer.boss.convert.UserConvert;
import com.summer.boss.exception.BusinessException;
import com.summer.boss.model.req.LoginParams;
import com.summer.boss.model.vo.MenuVO;
import com.summer.boss.orm.dao.PlatformUserMapper;
import com.summer.boss.orm.dao.SysUserRoleMapper;
import com.summer.boss.orm.entity.SysUser;
import com.summer.boss.orm.entity.SysMenu;
import com.summer.boss.orm.entity.SysUserRole;
import com.summer.boss.properties.BossProperties;
import com.summer.boss.service.ISysUserService;
import com.summer.boss.model.vo.UserVO;
import com.summer.boss.service.ISysMenuService;
import com.summer.boss.utils.JsonUtil;
import com.summer.boss.utils.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author john
 */
@Service
@Slf4j
public class SysUserServiceImpl extends ServiceImpl<PlatformUserMapper, SysUser> implements ISysUserService {

    @Resource
    private SysUserRoleMapper userRoleMapper;
    @Resource
    private UserConvert userConvert;
    @Resource
    private ISysMenuService sysMenuService;
    @Resource
    private MenuConvert menuConvert;
    @Resource
    private BossProperties bossProperties;
    @Resource
    private BCryptPasswordEncoder passwordEncoder;

    @Override
    public UserVO getUser(String username) {
        SysUser user = this.getOne(new QueryWrapper<SysUser>().eq(SysUser.USERNAME, username));
        if (ObjectUtils.isEmpty(user) || user.getId() == null) {
            return null;
        }

        UserVO userVO = this.userConvert.po2Vo(user);

        List<SysUserRole> roles = this.userRoleMapper.selectList(new QueryWrapper<SysUserRole>()
                .select(SysUserRole.ROLE_ID).eq(SysUserRole.USER_ID, user.getId()));

        List<Integer> roleIds = roles.stream().map(SysUserRole::getRoleId).collect(Collectors.toList());
        userVO.setRoles(roleIds);

        return userVO;
    }

    @Override
    public String login(LoginParams params) {
        // 配置的初始化用户登录逻辑
        if (CommonConstants.INIT_USERNAME.equals(params.getUsername())) {
            if (!passwordEncoder.matches(params.getPassword(), CommonConstants.INIT_USER_PASSWORD)) {
                throw new BusinessException(ResponseCodeEnum.PASSWORD_ERROR);
            }
            // 加载所有权限
            List<SysMenu> sysMenus = sysMenuService.getMenus(null, false);
            Set<MenuVO> menus = menuConvert.po2Vo(sysMenus);
            String key = String.format(CommonConstants.USER_PERMISSION, CommonConstants.INIT_USERNAME);
            RedisUtil.set(key, JsonUtil.stringify(menus), 2, TimeUnit.HOURS);

            // 生成 Token
            return JwtUtil.generateToken(new JwtInfo(0, CommonConstants.INIT_USERNAME), bossProperties.getAuth().getExpire());
        }

        // 系统正常用户登录逻辑，提取用户及角色信息
        UserVO user = this.getUser(params.getUsername());

        if (user != null) {
            if (!passwordEncoder.matches(params.getPassword(), user.getPassword())) {
                throw new BusinessException(ResponseCodeEnum.PASSWORD_ERROR);
            }

            List<Integer> roleIds = user.getRoles();

            // 2. 查询用户权限,并写入redis
            List<SysMenu> sysMenus = sysMenuService.getMenus(roleIds, true);
            Set<MenuVO> menus = menuConvert.po2Vo(sysMenus);
            String key = String.format(CommonConstants.USER_PERMISSION, user.getUsername());
            RedisUtil.set(key, JsonUtil.stringify(menus), 2, TimeUnit.HOURS);

            // 3. 生成 Token
            return JwtUtil.generateToken(new JwtInfo(user.getId(), user.getUsername()), bossProperties.getAuth().getExpire());
        } else {
            throw new BusinessException(ResponseCodeEnum.NO_USER);
        }
    }
}
